// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <nebula/types/meta/date_type.h>
#include <turbo/log/logging.h>

namespace nebula {

    constexpr Type::type Date32Type::type_id;

    constexpr Type::type Date64Type::type_id;

    constexpr Type::type Time32Type::type_id;

    constexpr Type::type Time64Type::type_id;

    constexpr Type::type TimestampType::type_id;

    constexpr Type::type MonthIntervalType::type_id;

    constexpr Type::type DayTimeIntervalType::type_id;

    constexpr Type::type MonthDayNanoIntervalType::type_id;

    constexpr Type::type DurationType::type_id;

    // ----------------------------------------------------------------------

    std::ostream &operator<<(std::ostream &os,
                             DayTimeIntervalType::DayMilliseconds interval) {
        os << interval.days << "d" << interval.milliseconds << "ms";
        return os;
    }

    std::ostream &operator<<(std::ostream &os,
                             MonthDayNanoIntervalType::MonthDayNanos interval) {
        os << interval.months << "M" << interval.days << "d" << interval.nanoseconds << "ns";
        return os;
    }

    // ----------------------------------------------------------------------
    // Date types

    DateType::DateType(Type::type type_id) : TemporalType(type_id) {}

    Date32Type::Date32Type() : DateType(Type::DATE32) {}

    Date64Type::Date64Type() : DateType(Type::DATE64) {}

    std::string Date64Type::to_string(bool show_metadata) const {
        return std::string("date64[ms]");
    }

    std::string Date32Type::to_string(bool show_metadata) const {
        return std::string("date32[day]");
    }


    // ----------------------------------------------------------------------
    // Time types

    TimeType::TimeType(Type::type type_id, TimeUnit::type unit)
            : TemporalType(type_id), unit_(unit) {}

    Time32Type::Time32Type(TimeUnit::type unit) : TimeType(Type::TIME32, unit) {
        KCHECK(unit == TimeUnit::SECOND || unit == TimeUnit::MILLI)
                        << "Must be seconds or milliseconds";
    }

    std::string Time32Type::to_string(bool show_metadata) const {
        std::stringstream ss;
        ss << "time32[" << this->unit_ << "]";
        return ss.str();
    }

    Time64Type::Time64Type(TimeUnit::type unit) : TimeType(Type::TIME64, unit) {
        KCHECK(unit == TimeUnit::MICRO || unit == TimeUnit::NANO)
                        << "Must be microseconds or nanoseconds";
    }

    std::string Time64Type::to_string(bool show_metadata) const {
        std::stringstream ss;
        ss << "time64[" << this->unit_ << "]";
        return ss.str();
    }





    // ----------------------------------------------------------------------
    // Timestamp types

    static char TimeUnitFingerprint(TimeUnit::type unit) {
        switch (unit) {
            case TimeUnit::SECOND:
                return 's';
            case TimeUnit::MILLI:
                return 'm';
            case TimeUnit::MICRO:
                return 'u';
            case TimeUnit::NANO:
                return 'n';
            default:
                DKCHECK(false) << "Unexpected TimeUnit";
                return '\0';
        }
    }


    // ----------------------------------------------------------------------
    // Fingerprint computations


    static char IntervalTypeFingerprint(IntervalType::type unit) {
        switch (unit) {
            case IntervalType::DAY_TIME:
                return 'd';
            case IntervalType::MONTHS:
                return 'M';
            case IntervalType::MONTH_DAY_NANO:
                return 'N';
            default:
                DKCHECK(false) << "Unexpected IntervalType::type";
                return '\0';
        }
    }

    std::string TimestampType::to_string(bool show_metadata) const {
        std::stringstream ss;
        ss << "timestamp[" << this->unit_;
        if (this->timezone_.size() > 0) {
            ss << ", tz=" << this->timezone_;
        }
        ss << "]";
        return ss.str();
    }

    // Duration types
    std::string DurationType::to_string(bool show_metadata) const {
        std::stringstream ss;
        ss << "duration[" << this->unit_ << "]";
        return ss.str();
    }

    std::string TimeType::compute_fingerprint() const {
        std::stringstream ss;
        ss << type_id_fingerprint(*this) << TimeUnitFingerprint(unit_);
        return ss.str();
    }

    std::string TimestampType::compute_fingerprint() const {
        std::stringstream ss;
        ss << type_id_fingerprint(*this) << TimeUnitFingerprint(unit_) << timezone_.length()
           << ':' << timezone_;
        return ss.str();
    }

    std::string IntervalType::compute_fingerprint() const {
        std::stringstream ss;
        ss << type_id_fingerprint(*this) << IntervalTypeFingerprint(interval_type());
        return ss.str();
    }

    std::string DurationType::compute_fingerprint() const {
        std::stringstream ss;
        ss << type_id_fingerprint(*this) << TimeUnitFingerprint(unit_);
        return ss.str();
    }

    std::string Date32Type::compute_fingerprint() const { return ::nebula::type_id_fingerprint(*this); }

    std::string Date64Type::compute_fingerprint() const { return ::nebula::type_id_fingerprint(*this); }


    const std::shared_ptr<DataType> &date64() {
        static std::shared_ptr<DataType> result = std::make_shared<Date64Type>();
        return result;
    }

    const std::shared_ptr<DataType> &date32() {
        static std::shared_ptr<DataType> result = std::make_shared<Date32Type>();
        return result;
    }

}  // namespace nebula

